
import GameEvent from './GameEvent';
import qrj_audio from './tools/qrj_audio';

const { ccclass, property } = cc._decorator;

@ccclass
export default abstract class BaseUI extends cc.Component {

    abstract baseUI_onLoad(): void;
    abstract baseUI_onDestroy(): void;
    abstract onBtn_click(target: cc.Button, onBtn_name: string): number | void;
    abstract init_data(d: any): void;

    onLoad(): void {
        if (this.node.getComponent(cc.Button)) {
            this.addClickEvent(this.node, this.node, cc.js.getClassName(this), "_onBtnClicked");
        }
        this._addClickEvent(this.node);
        this.baseUI_onLoad();
    }

    onDestroy() {
        this.m_objects.clear();
        GameEvent.unregister_with_ower(this);
        this.baseUI_onDestroy();
    }

    ///////// 递归查找 子节点
    private _findInChildren(node: cc.Node, name: string): cc.Node {
        let x: cc.Node = node.getChildByName(name);
        if (x) return x;
        if (node.childrenCount == 0) return null;

        for (let i = 0; i < node.childrenCount; ++i) {
            let tmp: cc.Node = this._findInChildren(node.children[i], name);
            if (tmp) return tmp;
        }
        return null;
    }

    ///////////////// 搜索/查找 子节点 并缓存
    private m_objects: Map<string, cc.Node> = new Map<string, cc.Node>();
    public GetGameObject(name: string, refind: boolean = false): cc.Node {
        if (!cc.isValid(this.node)) return null;
        if (!refind) {
            if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name);
            if (name == this.node.name) return this.node;
        }
        if (name.indexOf("/") != -1) {
            let tmp: cc.Node = cc.find(name, this.node);
            tmp && this.m_objects.set(name, tmp);
            return tmp;
        }
        else {
            let tmp = this._findInChildren(this.node, name);
            tmp && this.m_objects.set(name, tmp);
            return tmp;
        }
    }

    public GetGameComponent<T extends cc.Component>(name: string, type: { prototype: T }, refind: boolean = false): T {
        let n: cc.Node = this.GetGameObject(name, refind);
        if (n) return n.getComponent(type);
    }

    public GetSkeleton(name: string): sp.Skeleton {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(sp.Skeleton);
        let tmp: cc.Node = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(sp.Skeleton);
        return null;
    }

    public GetSprite(name: string): cc.Sprite {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.Sprite);
        let tmp: cc.Node = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.Sprite);
        return null;
    }

    public SetSprite(n: string | cc.Node, filepath: string): Promise<cc.SpriteFrame> {
        return new Promise((resolve, reject) => {
            let tmp: cc.Sprite = null;
            if (typeof n == "string") {
                tmp = this.GetSprite(n);
            } else {
                tmp = n.getComponent(cc.Sprite);
            }
            if (tmp) {
                BaseUI.load_res(filepath, cc.SpriteFrame).then((ret: cc.SpriteFrame) => {
                    if (cc.isValid(this.node)) {
                        tmp.spriteFrame = ret;
                        resolve(ret)
                    }
                })
            }
        })
    }

    public SetAtlasSprite(n: string | cc.Node, filepath: string, image: string): Promise<cc.SpriteFrame> {
        return new Promise((resolve, reject) => {
            let tmp: cc.Sprite = null;
            if (typeof n == "string") {
                tmp = this.GetSprite(n);
            } else {
                tmp = n.getComponent(cc.Sprite);
            }
            if (tmp) {
                BaseUI.load_res(filepath, cc.SpriteAtlas).then((ret: cc.SpriteAtlas) => {
                    if (cc.isValid(this.node)) {
                        let r = ret.getSpriteFrame(image);
                        tmp.spriteFrame = r
                        resolve(r)
                    }
                })
            }
        })
    }

    public SetUrlSprite(n: string | cc.Node, filepath: string): Promise<cc.SpriteFrame> {
        if (!filepath || filepath == "") {
            console.error("SetUrlSprite() 地址为空");
            return;
        };

        return new Promise((resolve, reject) => {
            let tmp: cc.Sprite = null;
            if (typeof n == "string") {
                tmp = this.GetSprite(n);
            } else {
                tmp = n.getComponent(cc.Sprite);
            }
            if (tmp) {
                BaseUI.load_url_SpriteFrame(filepath).then((ret: cc.SpriteFrame) => {
                    if (cc.isValid(this.node)) {
                        tmp.spriteFrame = ret;
                        resolve(ret)
                    }
                })
            }
        })
    }

    public GetLabel(name: string): cc.Label {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.Label);
        let tmp = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.Label);
        return null;
    }

    public SetLabel(TextID: string, content: string) {
        let t = this.GetLabel(TextID);
        if (t) {
            t.string = content;
        } else {
            cc.error("SetLabel 没有找到节点 : " + TextID);
        }
    }

    public GetRichText(name: string): cc.RichText {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.RichText);
        let tmp = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.RichText);
        return null;
    }

    public SetRichText(TextID: string, content: string) {
        let t = this.GetRichText(TextID)
        if (t) {
            t.string = content;
        } else {
            cc.error("SetRichText 没有找到节点 : " + TextID);
        }
    }

    public GetProgressBar(name: string): cc.ProgressBar {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.ProgressBar);
        let tmp = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.ProgressBar);
        return null;
    }

    public SetProgressBar(TextID: string, p: number) {
        let t = this.GetProgressBar(TextID);
        if (t) {
            t.progress = p;
        } else {
            cc.error("SetProgressBar 没有找到节点 : " + TextID);
        }
    }

    public GetButton(name: string): cc.Button {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.Button);
        let tmp = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.Button);
        return null;
    }

    public GetEditBox(name: string): cc.EditBox {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.EditBox);
        let tmp = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.EditBox);
        return null;
    }

    public SetEditBox(TextID: string, content: string) {
        let t = this.GetEditBox(TextID)
        if (t) {
            t.string = content;
        } else {
            cc.error("SetEditBox 没有找到节点 : " + TextID);
        }
    }

    public GetSlider(name: string): cc.Slider {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.Slider);
        let tmp = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.Slider);
        return null;
    }

    public SetSlider(TextID: string, progress: number) {
        let t = this.GetSlider(TextID);
        if (t) {
            t.progress = progress;
        } else {
            cc.error("SetSlider 没有找到节点 : " + TextID);
        }
    }

    public getScrollView(name: string): cc.ScrollView {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.ScrollView);
        let tmp = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.ScrollView);
        return null;
    }

    public GetToggle(name: string): cc.Toggle {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.Toggle);
        let tmp = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.Toggle);
        return null;
    }
    
    ///////////// 复选框 选择 
    public clickToggle(btn: cc.Button) {
        let togg: cc.Toggle = btn.getComponent(cc.Toggle);
        if (togg == null) return;

        if (btn.node.parent.getComponent(cc.ToggleContainer)) { /////////// 如果是单选按钮
            btn.node.parent.getComponent(cc.ToggleContainer).enabled = false;
            if (togg.isChecked == true) return;

            let p = btn.node.parent;
            let toggles = p.getComponentsInChildren(cc.Toggle);
            for (let i = 0; i < toggles.length; i++) {
                toggles[i].isChecked = toggles[i] == togg;
            }
        } else { ////////// 复选按钮
            togg.isChecked = !togg.isChecked;
        }
    }

    public SetToggle(name: string, checked: boolean = null) {
        let t = this.GetToggle(name);
        if (t) {
            if (checked == null) {
                checked = !t.isChecked;
            } else {
                t.isChecked = checked;
            }
        } else {
            cc.error("SetToggle 没有找到节点 : " + name);
        }
    }


    public GetPageView(name: string): cc.PageView {
        if (this.m_objects && this.m_objects.has(name)) return this.m_objects.get(name).getComponent(cc.PageView);
        let tmp = this.GetGameObject(name);
        if (tmp) return tmp.getComponent(cc.PageView);
        return null;
    }


    public SetPageView(TextID: string, page: number) {
        let t = this.GetPageView(TextID)
        if (t) {
            t.setCurrentPageIndex(page);
        } else {
            cc.error("SetPageView 没有找到节点 : " + TextID);
        }
    }


    closeUI() {
        if (this.node) {
            this.node.active = false;
            this.node.parent = null;
            this.node.removeFromParent();
        }
    }

    closeAllUI() {
        let ui = cc.find("Canvas");
        for (let i = 0; i < ui.childrenCount; ++i) {
            let baseui = ui.children[i].getComponent(BaseUI);
            if (baseui) baseui.closeUI();
        }
    }


    /////////////////////////////////////////////////// 事件
    add_event(type: string, callFunc: Function) {
        GameEvent.register(this, type, callFunc);
    }

    set_event(type: string, callFunc: Function) {
        GameEvent.unregister_with_type(type);
        GameEvent.register(this, type, callFunc);
    }

    del_event(type: string) {
        GameEvent.unregister_with_type(type);
    }

    dispatch_event(type: string, data: any = null) {
        GameEvent.dispatch(type, data);
    }
    //////////////////////////////////////////////////////

    public static load_res(path: string, type: typeof cc.Asset): Promise<cc.Asset> {
        return new Promise((resolve, reject) => {
            cc.resources.load(path, type, (err, ret) => {
                if (err) {
                    cc.error(path, err);
                    reject(err);
                }
                else {
                    resolve(ret);
                }
            })
        })
    }

    public static load_url_SpriteFrame(path: string): Promise<cc.SpriteFrame> {
        if (!path || path == "") {
            console.error("_load() 地址为空");
            return;
        };
        return new Promise((resolve, reject) => {
            //////////////////////////// 2.4 版本
            if (cc.ENGINE_VERSION.startsWith("2.4.") == true) {
                if (path.endsWith('.jpg') || path.endsWith('.png')) {
                    cc.assetManager.loadRemote(path, (err, texture: cc.Texture2D) => {
                        if (err) {
                            cc.error(path, err);
                            reject(null);
                        }
                        else {
                            let spr = new cc.SpriteFrame(texture);
                            resolve(spr);
                        }
                    });
                } else {
                    cc.assetManager.loadRemote(path, { ext: '.png' }, (err, texture: cc.Texture2D) => {
                        if (err) {
                            cc.error(path, err);
                            reject(null);
                        }
                        else {
                            let spr = new cc.SpriteFrame(texture);
                            resolve(spr);
                        }
                    });
                }
                return;
            }
            ///////////////////////////////////2.4以下版本
            if (path.endsWith('.jpg') || path.endsWith('.png')) {
                cc.loader.load(path, (err, ret) => {
                    if (err) {
                        cc.error(path, err);
                        reject(null);
                    }
                    else {
                        let spr = new cc.SpriteFrame(ret);
                        resolve(spr);
                    }
                })
            } else {
                cc.loader.load({ url: path, type: 'png' }, (err, ret) => {
                    if (err) {
                        cc.error(path, err);
                        reject(null);
                    }
                    else {
                        let spr = new cc.SpriteFrame(ret);
                        resolve(spr);
                    }
                })
            }
        })
    }

    createUI(filepath: string, parent: cc.Node = null, par: any = null): Promise<cc.Node> {
        return new Promise((resolve, reject) => {
            cc.resources.load(filepath, cc.Prefab, (err, ret) => {
                if (err) {
                    console.error(err)
                    reject();
                    return;
                }

                if (parent == null) {
                    parent = cc.find("Canvas")
                }

                let index: number = filepath.lastIndexOf("/");
                let name = filepath.substring(index + 1, filepath.length);
                if (parent.getComponentInChildren(name)) {
                    console.log("重复UI跳过")
                    return
                }

                /////////////////////////////////////////////////
                let tmp: cc.Node = cc.instantiate(ret);
                let bui: BaseUI = tmp.getComponent(BaseUI);
                if (!bui) {
                    console.error("没有找到BaseUI脚本,ui创建失败", filepath);
                    return;
                }

                if (par == null) { ////// 没有传递参数
                    bui && bui.init_data(null);
                    tmp.parent = parent;
                    resolve(tmp);
                    return;
                }

                if (typeof par == "object") {
                    if (par.def_delay == true) { ///// 如果有延迟参数
                        tmp.opacity = 0;
                        cc.tween(tmp)
                            .delay(0.01)
                            .call(() => {
                                tmp.opacity = 255;
                            })
                            .start();
                    }
                    if (par.succ_event && typeof par.succ_event == "function") {
                        par.succ_event(bui);
                    }
                }

                bui.init_data(par);
                tmp.parent = parent;
                resolve(tmp);
            })
        })
    }

    createPrefab(filepath: string, parent: cc.Node = null, callback: Function, pos: cc.Vec3 = null): Promise<cc.Node> {
        return new Promise((resolve, reject) => {
            cc.resources.load(filepath, cc.Prefab, (err, ret) => {
                if (err) {
                    console.error(err)
                    reject();
                    return;
                }

                if (parent == null) {
                    parent = cc.find("Canvas")
                }

                let tmp: cc.Node = cc.instantiate(ret);

                tmp.parent = parent;
                if (pos) {
                    tmp.position = pos;
                }

                if (callback) {
                    callback(tmp);
                }
                resolve(tmp);
            })
        })
    }

    /******************************************************
     *                  内置 按钮事件 处理
     ******************************************************/
    private _addClickEvent(node: cc.Node) {
        if (this._isSkipNode(node)) return;
        for (let i = 0; i < node.childrenCount; ++i) {
            let tmp = node.children[i];
            if (this._isSkipNode(tmp)) continue;
            if (tmp.getComponent(cc.Button)) {
                this.addClickEvent(tmp, this.node, cc.js.getClassName(this), "_onBtnClicked");
            }
            this._addClickEvent(tmp);
        }
    }

    private _isSkipNode(node: cc.Node): boolean {
        if (this.node == node) {
            return false;
        }
        let b = node.getComponent(BaseUI);
        return b != null;
    }

    //////////////// 内置按钮事件
    private _onBtnClicked(event) {

        ////////////////// 按钮前事件
        let btn = event.target.getComponent(cc.Button)
        let dt = this.onBtn_click(btn, event.target.name) || 0.2;
        btn.enabled = false;
        if (dt >= 0) {
            this.scheduleOnce(() => { btn.enabled = true }, dt);
        }
        ////////////////// 按钮后事件

        // qrj_audio.playAudio("click");
    }

    private addClickEvent(node: cc.Node, target: cc.Node, component: string, handler: string) {
        console.log("页面内按钮 " + this.node.name + "-->" + node.name);
        let clickEvents = node.getComponent(cc.Button).clickEvents;
        if (clickEvents.length > 0) {
            if (!CC_EDITOR)
                cc.warn("按钮已经存在绑定，跳过自动绑定", node.name);
            return;
        }

        let eventHandler = new cc.Component.EventHandler();
        eventHandler.target = target;
        eventHandler.component = component;
        eventHandler.handler = handler;
        // if (customEventData) eventHandler.customEventData = customEventData;

        clickEvents.push(eventHandler);
    }

}
